package name.puzio.math;

public final class ComplexNumber {
	private final double imaginary;
	private final double real;

	// cache intern
	private final double amount;
	private final double angle;

	@Override
	public final boolean equals(Object object) {
		if (!(object instanceof ComplexNumber))
			return false;
		ComplexNumber a = (ComplexNumber) object;
		return (real == a.real) && (imaginary == a.imaginary);
	}

	public static final ComplexNumber newComplexNumberFromPolar(double amount, double angle) {
		return new ComplexNumber(amount * Math.cos(angle), amount
				* Math.sin(angle), amount, angle);
	}

	public ComplexNumber(double real, double imaginary) {
		this(//
				real,//
				imaginary,//
				Math.sqrt((real * real) + (imaginary * imaginary)),//
				Math.atan2(imaginary, real));
	}

	private ComplexNumber(double real, double imaginary, double amount,
			double angle) {
		this.real = real;
		this.imaginary = imaginary;
		this.amount = amount;
		this.angle = angle;

	}

	public final double getImaginary() {
		return imaginary;
	}

	public final double getReal() {
		return real;
	}

	public final double getAmount() {
		return amount;
	}

	public final double getAngle() {
		return angle;
	}

	public final ComplexNumber add(ComplexNumber b) {
		return add(this, b);
	}

	public final ComplexNumber sub(ComplexNumber b) {
		return sub(this, b);
	}

	public final ComplexNumber div(ComplexNumber b) {
		return div(this, b);
	}

	public final ComplexNumber mul(ComplexNumber b) {
		return mul(this, b);
	}

	public final ComplexNumber conjugation() {
		return conjugation(this);
	}

	public final ComplexNumber pow(int exponent) {
		return pow(this, exponent);
	}

	/**
	 * Addition:
	 * 
	 * @param a
	 * @param b
	 * @return
	 */
	public final static synchronized ComplexNumber add(ComplexNumber a,
			ComplexNumber b) {
		return new ComplexNumber(a.real + b.real, a.imaginary + b.imaginary);
	}

	/**
	 * Subtraktion:
	 * 
	 * @param a
	 * @param b
	 * @return
	 */
	public final static synchronized ComplexNumber sub(ComplexNumber a,
			ComplexNumber b) {
		return new ComplexNumber(a.real - b.real, a.imaginary - b.imaginary);
	}

	/**
	 * Multiplikation:
	 * 
	 * @param a
	 * @param b
	 * @return
	 **/
	public final static synchronized ComplexNumber mul(ComplexNumber a,
			ComplexNumber b) {
		return new ComplexNumber((a.real * b.real)
				- (a.imaginary * b.imaginary), (a.imaginary * b.real)
				+ (a.real * b.imaginary));
	}

	/**
	 * Division:
	 * 
	 * @param a
	 * @param b
	 * @return
	 **/
	public final static synchronized ComplexNumber div(ComplexNumber a,
			ComplexNumber b) {
		double d = (b.real * b.real) + (b.imaginary * b.imaginary);
		if (d == 0)
			return new ComplexNumber(Double.NaN, Double.NaN);
		return new ComplexNumber(
				((a.real * b.real) + (a.imaginary * b.imaginary)) / d,
				((a.imaginary * b.real) - (a.real * b.imaginary)) / d);
	}

	/**
	 * Konjugation:
	 * 
	 * @param a
	 * @return
	 **/

	public final static synchronized ComplexNumber conjugation(ComplexNumber a) {
		return new ComplexNumber(a.real, -a.imaginary);
	}

	/**
	 * Pow:
	 * 
	 * @param a
	 * @param exponent
	 * @return
	 */
	public final static synchronized ComplexNumber pow(ComplexNumber a,
			int exponent) {
		return newComplexNumberFromPolar(Math.pow(a.amount, exponent), a.angle * exponent);
	}
}
